package com.fox.imagedb.ui;

/*
 * @(#)DefaultTreeCellRenderer.java	1.61 07/01/08
 *
 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Rectangle;

import javax.swing.Icon;
import javax.swing.JLabel;
import javax.swing.JTree;
import javax.swing.UIManager;
import javax.swing.plaf.ColorUIResource;
import javax.swing.plaf.FontUIResource;
import javax.swing.plaf.basic.BasicGraphicsUtils;
import javax.swing.tree.TreeCellRenderer;

/**
 * Displays an entry in a tree. <code>DefaultTreeCellRenderer</code> is not
 * opaque and unless you subclass paint you should not change this. See <a
 * href="http://java.sun.com/docs/books/tutorial/uiswing/components/tree.html"
 * >How to Use Trees</a> in <em>The Java Tutorial</em> for examples of
 * customizing node display using this class.
 * <p>
 * 
 * <strong><a name="override">Implementation Note:</a></strong> This class
 * overrides <code>invalidate</code>, <code>validate</code>,
 * <code>revalidate</code>, <code>repaint</code>, and
 * <code>firePropertyChange</code> solely to improve performance. If not
 * overridden, these frequently called methods would execute code paths that are
 * unnecessary for the default tree cell renderer. If you write your own
 * renderer, take care to weigh the benefits and drawbacks of overriding these
 * methods.
 * 
 * <p>
 * <strong>Warning:</strong> Serialized objects of this class will not be
 * compatible with future Swing releases. The current serialization support is
 * appropriate for short term storage or RMI between applications running the
 * same version of Swing. As of 1.4, support for long term storage of all
 * JavaBeans<sup><font size="-2">TM</font></sup> has been added to the
 * <code>java.beans</code> package. Please see {@link java.beans.XMLEncoder}.
 * 
 * @version 1.61 01/08/07
 * @author Rob Davis
 * @author Ray Ryan
 * @author Scott Violet
 */
public class PhylTreeCellRenderer extends JLabel implements TreeCellRenderer {

	private static final long serialVersionUID = 204913544201899753L;

	/** Last tree the renderer was painted in. */
	private JTree tree;

	/** Is the value currently selected. */
	protected boolean selected;

	/** True if has focus. */
	protected boolean hasFocus;

	/** True if draws focus border around icon as well. */
	private boolean drawsFocusBorderAroundIcon;

	/** If true, a dashed line is drawn as the focus indicator. */
	private boolean drawDashedFocusIndicator;

	// If drawDashedFocusIndicator is true, the following are used.
	/**
	 * Background color of the tree.
	 */
	private Color treeBGColor;

	/**
	 * Color to draw the focus indicator in, determined from the background.
	 * color.
	 */
	private Color focusBGColor;

	// Colors
	/** Color to use for the foreground for selected nodes. */
	protected Color textSelectionColor;

	/** Color to use for the foreground for non-selected nodes. */
	protected Color textNonSelectionColor;

	/** Color to use for the background when a node is selected. */
	protected Color backgroundSelectionColor;

	/** Color to use for the background when the node isn't selected. */
	protected Color backgroundNonSelectionColor;

	/** Color to use for the focus indicator when the node has focus. */
	protected Color borderSelectionColor;

	private boolean isDropCell;

	/**
	 * Returns a new instance of DefaultTreeCellRenderer. Alignment is set to
	 * left aligned. Icons and text color are determined from the UIManager.
	 */
	public PhylTreeCellRenderer() {
		setTextSelectionColor(UIManager.getColor("Tree.selectionForeground"));
		setTextNonSelectionColor(UIManager.getColor("Tree.textForeground"));
		setBackgroundSelectionColor(UIManager
				.getColor("Tree.selectionBackground"));
		setBackgroundNonSelectionColor(UIManager
				.getColor("Tree.textBackground"));
		setBorderSelectionColor(UIManager.getColor("Tree.selectionBorderColor"));
		Object value = UIManager.get("Tree.drawsFocusBorderAroundIcon");
		drawsFocusBorderAroundIcon = (value != null && ((Boolean) value)
				.booleanValue());
		value = UIManager.get("Tree.drawDashedFocusIndicator");
		drawDashedFocusIndicator = (value != null && ((Boolean) value)
				.booleanValue());
	}

	/**
	 * Returns the default icon, for the current laf, that is used to represent
	 * non-leaf nodes that are expanded.
	 */
	public Icon getDefaultOpenIcon() {
		return UIManager.getIcon("Tree.openIcon");
	}

	/**
	 * Returns the default icon, for the current laf, that is used to represent
	 * non-leaf nodes that are not expanded.
	 */
	public Icon getDefaultClosedIcon() {
		return UIManager.getIcon("Tree.closedIcon");
	}

	/**
	 * Returns the default icon, for the current laf, that is used to represent
	 * leaf nodes.
	 */
	public Icon getDefaultLeafIcon() {
		return UIManager.getIcon("Tree.leafIcon");
	}

	/**
	 * Sets the color the text is drawn with when the node is selected.
	 */
	public void setTextSelectionColor(Color newColor) {
		textSelectionColor = newColor;
	}

	/**
	 * Returns the color the text is drawn with when the node is selected.
	 */
	public Color getTextSelectionColor() {
		return textSelectionColor;
	}

	/**
	 * Sets the color the text is drawn with when the node isn't selected.
	 */
	public void setTextNonSelectionColor(Color newColor) {
		textNonSelectionColor = newColor;
	}

	/**
	 * Returns the color the text is drawn with when the node isn't selected.
	 */
	public Color getTextNonSelectionColor() {
		return textNonSelectionColor;
	}

	/**
	 * Sets the color to use for the background if node is selected.
	 */
	public void setBackgroundSelectionColor(Color newColor) {
		backgroundSelectionColor = newColor;
	}

	/**
	 * Returns the color to use for the background if node is selected.
	 */
	public Color getBackgroundSelectionColor() {
		return backgroundSelectionColor;
	}

	/**
	 * Sets the background color to be used for non selected nodes.
	 */
	public void setBackgroundNonSelectionColor(Color newColor) {
		backgroundNonSelectionColor = newColor;
	}

	/**
	 * Returns the background color to be used for non selected nodes.
	 */
	public Color getBackgroundNonSelectionColor() {
		return backgroundNonSelectionColor;
	}

	/**
	 * Sets the color to use for the border.
	 */
	public void setBorderSelectionColor(Color newColor) {
		borderSelectionColor = newColor;
	}

	/**
	 * Returns the color the border is drawn.
	 */
	public Color getBorderSelectionColor() {
		return borderSelectionColor;
	}

	/**
	 * Subclassed to map <code>FontUIResource</code>s to null. If
	 * <code>font</code> is null, or a <code>FontUIResource</code>, this has the
	 * effect of letting the font of the JTree show through. On the other hand,
	 * if <code>font</code> is non-null, and not a <code>FontUIResource</code>,
	 * the font becomes <code>font</code>.
	 */
	public void setFont(Font font) {
		if (font instanceof FontUIResource)
			font = null;
		super.setFont(font);
	}

	/**
	 * Gets the font of this component.
	 * 
	 * @return this component's font; if a font has not been set for this
	 *         component, the font of its parent is returned
	 */
	public Font getFont() {
		Font font = super.getFont();

		if (font == null && tree != null) {
			// Strive to return a non-null value, otherwise the html support
			// will typically pick up the wrong font in certain situations.
			font = tree.getFont();
		}
		return font;
	}

	/**
	 * Subclassed to map <code>ColorUIResource</code>s to null. If
	 * <code>color</code> is null, or a <code>ColorUIResource</code>, this has
	 * the effect of letting the background color of the JTree show through. On
	 * the other hand, if <code>color</code> is non-null, and not a
	 * <code>ColorUIResource</code>, the background becomes <code>color</code>.
	 */
	public void setBackground(Color color) {
		if (color instanceof ColorUIResource)
			color = null;
		super.setBackground(color);
	}

	/**
	 * Configures the renderer based on the passed in components. The value is
	 * set from messaging the tree with <code>convertValueToText</code>, which
	 * ultimately invokes <code>toString</code> on <code>value</code>. The
	 * foreground color is set based on the selection and the icon is set based
	 * on the <code>leaf</code> and <code>expanded</code> parameters.
	 */
	public Component getTreeCellRendererComponent(JTree tree, Object value,
			boolean sel, boolean expanded, boolean leaf, int row,
			boolean hasFocus) {
		String stringValue = tree.convertValueToText(value, sel, expanded,
				leaf, row, hasFocus);

		this.tree = tree;
		this.hasFocus = hasFocus;
		if (stringValue.equals("")) {
			stringValue = null;
		}
		setText(stringValue);

		Color fg = null;
		isDropCell = false;

		JTree.DropLocation dropLocation = tree.getDropLocation();
		if (dropLocation != null && dropLocation.getChildIndex() == -1
				&& tree.getRowForPath(dropLocation.getPath()) == row) {

			Color col = UIManager.getColor("Tree.dropCellForeground");
			if (col != null) {
				fg = col;
			} else {
				fg = getTextSelectionColor();
			}

			isDropCell = true;
		} else if (sel) {
			fg = getTextSelectionColor();
		} else {
			fg = getTextNonSelectionColor();
		}

		setForeground(fg);

		setComponentOrientation(tree.getComponentOrientation());

		selected = sel;

		return this;
	}

	/**
	 * Paints the value. The background is filled based on selected.
	 */
	public void paint(Graphics g) {
		Color bColor;

		if (isDropCell) {
			bColor = UIManager.getColor("Tree.dropCellBackground");
			if (bColor == null) {
				bColor = getBackgroundSelectionColor();
			}
		} else if (selected) {
			bColor = getBackgroundSelectionColor();
		} else {
			bColor = getBackgroundNonSelectionColor();
			if (bColor == null) {
				bColor = getBackground();
			}
		}

		int imageOffset = -1;
		if (bColor != null) {

			imageOffset = getLabelStart();
			g.setColor(bColor);
			if (getComponentOrientation().isLeftToRight()) {
				g.fillRect(imageOffset, 0, getWidth() - imageOffset,
						getHeight());
			} else {
				g.fillRect(0, 0, getWidth() - imageOffset, getHeight());
			}
		}

		if (hasFocus) {
			if (drawsFocusBorderAroundIcon) {
				imageOffset = 0;
			} else if (imageOffset == -1) {
				imageOffset = getLabelStart();
			}
			if (getComponentOrientation().isLeftToRight()) {
				paintFocus(g, imageOffset, 0, getWidth() - imageOffset,
						getHeight(), bColor);
			} else {
				paintFocus(g, 0, 0, getWidth() - imageOffset, getHeight(),
						bColor);
			}
		}
		super.paint(g);
	}

	private void paintFocus(Graphics g, int x, int y, int w, int h,
			Color notColor) {
		Color bsColor = getBorderSelectionColor();

		if (bsColor != null && (selected || !drawDashedFocusIndicator)) {
			g.setColor(bsColor);
			g.drawRect(x, y, w - 1, h - 1);
		}
		if (drawDashedFocusIndicator && notColor != null) {
			if (treeBGColor != notColor) {
				treeBGColor = notColor;
				focusBGColor = new Color(~notColor.getRGB());
			}
			g.setColor(focusBGColor);
			BasicGraphicsUtils.drawDashedRect(g, x, y, w, h);
		}
	}

	private int getLabelStart() {
		Icon currentI = getIcon();
		if (currentI != null && getText() != null) {
			return currentI.getIconWidth() + Math.max(0, getIconTextGap() - 1);
		}
		return 0;
	}

	/**
	 * Overrides <code>JComponent.getPreferredSize</code> to return slightly
	 * wider preferred size value.
	 */
	public Dimension getPreferredSize() {
		Dimension retDimension = super.getPreferredSize();

		if (retDimension != null)
			retDimension = new Dimension(retDimension.width + 3,
					retDimension.height);
		return retDimension;
	}

	/**
	 * Overridden for performance reasons. See the <a
	 * href="#override">Implementation Note</a> for more information.
	 */
	public void validate() {
	}

	/**
	 * Overridden for performance reasons. See the <a
	 * href="#override">Implementation Note</a> for more information.
	 * 
	 * @since 1.5
	 */
	public void invalidate() {
	}

	/**
	 * Overridden for performance reasons. See the <a
	 * href="#override">Implementation Note</a> for more information.
	 */
	public void revalidate() {
	}

	/**
	 * Overridden for performance reasons. See the <a
	 * href="#override">Implementation Note</a> for more information.
	 */
	public void repaint(long tm, int x, int y, int width, int height) {
	}

	/**
	 * Overridden for performance reasons. See the <a
	 * href="#override">Implementation Note</a> for more information.
	 */
	public void repaint(Rectangle r) {
	}

	/**
	 * Overridden for performance reasons. See the <a
	 * href="#override">Implementation Note</a> for more information.
	 * 
	 * @since 1.5
	 */
	public void repaint() {
	}

	/**
	 * Overridden for performance reasons. See the <a
	 * href="#override">Implementation Note</a> for more information.
	 */
	protected void firePropertyChange(String propertyName, Object oldValue,
			Object newValue) {
		// Strings get interned...
		if (propertyName == "text"
				|| ((propertyName == "font" || propertyName == "foreground")
						&& oldValue != newValue && getClientProperty(javax.swing.plaf.basic.BasicHTML.propertyKey) != null)) {

			super.firePropertyChange(propertyName, oldValue, newValue);
		}
	}

	/**
	 * Overridden for performance reasons. See the <a
	 * href="#override">Implementation Note</a> for more information.
	 */
	public void firePropertyChange(String propertyName, byte oldValue,
			byte newValue) {
	}

	/**
	 * Overridden for performance reasons. See the <a
	 * href="#override">Implementation Note</a> for more information.
	 */
	public void firePropertyChange(String propertyName, char oldValue,
			char newValue) {
	}

	/**
	 * Overridden for performance reasons. See the <a
	 * href="#override">Implementation Note</a> for more information.
	 */
	public void firePropertyChange(String propertyName, short oldValue,
			short newValue) {
	}

	/**
	 * Overridden for performance reasons. See the <a
	 * href="#override">Implementation Note</a> for more information.
	 */
	public void firePropertyChange(String propertyName, int oldValue,
			int newValue) {
	}

	/**
	 * Overridden for performance reasons. See the <a
	 * href="#override">Implementation Note</a> for more information.
	 */
	public void firePropertyChange(String propertyName, long oldValue,
			long newValue) {
	}

	/**
	 * Overridden for performance reasons. See the <a
	 * href="#override">Implementation Note</a> for more information.
	 */
	public void firePropertyChange(String propertyName, float oldValue,
			float newValue) {
	}

	/**
	 * Overridden for performance reasons. See the <a
	 * href="#override">Implementation Note</a> for more information.
	 */
	public void firePropertyChange(String propertyName, double oldValue,
			double newValue) {
	}

	/**
	 * Overridden for performance reasons. See the <a
	 * href="#override">Implementation Note</a> for more information.
	 */
	public void firePropertyChange(String propertyName, boolean oldValue,
			boolean newValue) {
	}

}
